package com.lianlianpay;

import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.lianlianpay.bean.ConfirmPaymentRequestBean;
import com.lianlianpay.bean.ConfirmPaymentResponseBean;
import com.lianlianpay.bean.PaymentRequestBean;
import com.lianlianpay.bean.PaymentResponseBean;
import com.lianlianpay.bean.QueryPaymentRequestBean;
import com.lianlianpay.bean.QueryPaymentResponseBean;
import com.lianlianpay.constant.PaymentConstant;
import com.lianlianpay.constant.PaymentStatusEnum;
import com.lianlianpay.constant.RetCodeEnum;
import com.lianlianpay.security.utils.LianLianPaySecurity;
import com.lianlianpay.util.HttpUtil;
import com.lianlianpay.util.SignUtil;
import com.lianpay.api.util.TraderRSAUtil;
import com.seek.cache.CacheUtil;
import com.seek.common.handler.SpringContextHolder;
import com.seek.constants.RedisCustomerConstant;
import com.seek.inter.interdto.InterDtoCUser;
import com.seek.model.entity.TBizFlow;
import com.seek.model.entity.TLoan;
import com.seek.service.BizFlowService;
import com.seek.service.LoanService;
import com.seek.util.ConstantsUtil;

/**
 * 实时付款对接流程：开通商户号，登录商户站上传商户生成的公钥，商户服务器IP需要向连连报备（群里咨询，会发报备申请表），
 * 代码对接好了后，如在连连商户账户没钱，需先在连连商户站充值（可采用线下加款方式），用正式商户号和真实数据测试
 */
public class PaymentApi {

	private static final Logger logger = LoggerFactory.getLogger(PaymentApi.class);

	private static BizFlowService bizFlowService = (BizFlowService) SpringContextHolder.getBean("bizFlowService");

	private static LoanService loanService = (LoanService) SpringContextHolder.getBean("loanService");

	/**
	 * 实时付款正常流程只需要调用付款交易接口，同步返回0000时指创建连连支付单成功，订单处于付款处理中，等待连连异步通知告知付款状态
	 *
	 * @param args
	 * @throws Exception
	 */
	public String lianPayMent(TLoan tloan) throws Exception {
		String response = "";
		try {
			// 根据ID，获取个人用户信息
			InterDtoCUser cust = CacheUtil.getCLoginCache(
					CacheUtil.getRedisData(RedisCustomerConstant.CUST_KEY_PREFIX + tloan.getCustomerId()));
			if (null == cust || StringUtils.isEmpty(cust.getCustName())) {
				logger.error("客户{}缓存信息不存在！", tloan.getCustomerId());
				throw new Exception("客户" + tloan.getCustomerId() + "缓存信息不存在！");
			}
			tloan.setIdName(cust.getCustName());
			// 参数封装,并转换成json字符串
			String jsonStr = encapsulation(tloan);
			logger.info("实时付款请求报文：" + jsonStr);
			// size异常时，可参考这个网页解决问题http://www.wxdl.cn/java/security-invalidkey-exception.html
			String encryptStr = LianLianPaySecurity.encrypt(jsonStr, PaymentConstant.PUBLIC_KEY_ONLINE);
			if (StringUtils.isEmpty(encryptStr)) {
				logger.error("加密异常:");
				return "999";
			}
			JSONObject json = new JSONObject();
			json.put("oid_partner", PaymentConstant.OID_PARTNER);
			json.put("pay_load", encryptStr);
			addBizFlow(tloan);// 拉起一条调用第三方接口的记录
			loanService.updateTloan(tloan.getId().intValue(), 1);
			response = HttpUtil.doPost(PaymentConstant.URL, json, "UTF-8");
		} catch (Exception e) {
			e.printStackTrace();
		}
		logger.info("付款接口返回响应报文：{}", response);

		if (StringUtils.isEmpty(response)) {
			queryPaymentAndDealBusiness(tloan.getOrderNo(),"");
		} else {
			PaymentResponseBean paymentResponseBean = JSONObject.parseObject(response, PaymentResponseBean.class);
			logger.info("PaymentResponseBean：{}", JSONObject.toJSONString(paymentResponseBean));
			JSONObject repJson = JSONObject.parseObject(response);
			if (paymentResponseBean.getRet_code().equals("0000")) {
				// 先对结果验签

				boolean signCheck = TraderRSAUtil.checksign(PaymentConstant.PUBLIC_KEY_ONLINE,
						SignUtil.genSignData(repJson), paymentResponseBean.getSign());
				if (!signCheck) {
					logger.error("返回结果验签异常,可能数据被篡改");
					response = "返回结果验签异常,可能数据被篡改";
					return response;
				}
				logger.info(tloan.getOrderNo() + "订单处于付款处理中");
				// 成功不做任何处理，因为是同步的，处理是在异步里面进行操作的
			} else if (paymentResponseBean.getRet_code().equals("4002")) {
				submitPayment(paymentResponseBean.getNo_order(), repJson.getString("confirm_code"));
			}else if( paymentResponseBean.getRet_code().equals("4004")|| paymentResponseBean.getRet_code().equals("4003")){
				String loanSql = "update t_loan set down_type = 1 ,down_time = now(),loan_status = 4 ,down_remark = ? , executionstatus= 0  where id = ?";
				try {
					loanService.executeUpdate(loanSql,
							new Object[] { paymentResponseBean.getRet_msg(), tloan.getId() });
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}else if (RetCodeEnum.isNeedQuery(paymentResponseBean.getRet_code())) {
//				String loanSql = "update t_loan set  executionstatus= 1  where id = ?";
//				loanService.executeUpdate(loanSql,
//						new Object[] {tloan.getId() });
				queryPaymentAndDealBusiness(paymentResponseBean.getNo_order(),"");
			} else {
				String loanSql = "update t_loan set down_type = 1 ,down_time = now(),loan_status = 4 ,down_remark = ? , executionstatus= 0  where id = ?";
				try {
					loanService.executeUpdate(loanSql,
							new Object[] { paymentResponseBean.getRet_msg(), tloan.getId() });
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		return response;
	}

	// 放款参数封装
	private String encapsulation(TLoan tloan) {
		PaymentRequestBean paymentRequestBean = new PaymentRequestBean();
		paymentRequestBean.setNo_order(tloan.getOrderNo());// 交易流水号20170629165934
		paymentRequestBean.setDt_order(new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()));// dt_order付款时间
		paymentRequestBean.setMoney_order(
				new DecimalFormat("#0.00").format(ConstantsUtil.DEBUG_MODE == 1 ? (0.01) : tloan.getDownMoney()*1.00 / 100));// 付款金额
																														// 应该取借款记录表中的借款金额tloan.getDownMoney()/100
		paymentRequestBean.setCard_no(tloan.getBankNo());// 银行账号
		paymentRequestBean.setAcct_name(tloan.getIdName()); // 银行卡管理表中的银行卡户名
		paymentRequestBean.setBank_name(tloan.getBankName());// 根据银行卡号自动匹配所属银行和卡片的类型
//		paymentRequestBean.setInfo_order("放款：" + tloan.getDownMoney() + "？");// 付款用途
		paymentRequestBean.setFlag_card("0");// 对公对私标志 0-对私 1-对公
		paymentRequestBean.setMemo("扣除手续费" + tloan.getHandleFee() *1.00/ 100 + "后，实际放款金额:" + tloan.getDownMoney() / 100);// 收款备注
																													// 用于给用户显示
		paymentRequestBean.setNotify_url(PaymentConstant.LIANLIAN_FK_URL);// 异步回调地址
		paymentRequestBean.setOid_partner(PaymentConstant.OID_PARTNER);// 商户号
		paymentRequestBean.setApi_version(PaymentConstant.API_VERSION);// 版本号
		paymentRequestBean.setSign_type(PaymentConstant.SIGN_TYPE);// 加签方式RSA
		paymentRequestBean.setSign(SignUtil.genRSASign(JSON.parseObject(JSON.toJSONString(paymentRequestBean))));
		String jsonStr = JSON.toJSONString(paymentRequestBean);
		return jsonStr;
	}

	// 拉起还款对接tbizflow表
	private void addBizFlow(TLoan tloan) {
		TBizFlow bizFlow = new TBizFlow();
		bizFlow.setCompanyId(tloan.getCompanyId());
		bizFlow.setCustomerId(tloan.getCustomerId());
		bizFlow.setBizNo(tloan.getOrderNo());
		bizFlow.setBizType("lianlianPay");
		bizFlow.setOrgCode("LIANLIAN");
		bizFlow.setDeleted(0);
		bizFlow.setOrgBizNo("");
		bizFlow.setStatus("");
		bizFlow.setCreateTime(new Date());
		bizFlow.setModifyTime(new Date());
		tbizflowHelp(bizFlow);// 添加流水

	}

	// 异常时，先查询订单状态，再根据订单状态处理业务逻辑
	public void queryPaymentAndDealBusiness(String orderNo, String confirmCode) throws Exception {
		String resultPay = "";//SUCCESS 付款成功 FAILURE 付款失败 CANCEL 付款退款
		String oidPaybill = "";//第三方订单号
		// 连连内部测试环境数据
		QueryPaymentRequestBean queryRequestBean = new QueryPaymentRequestBean();
		queryRequestBean.setNo_order(orderNo);
		queryRequestBean.setOid_partner(PaymentConstant.OID_PARTNER);
		queryRequestBean.setApi_version(PaymentConstant.API_VERSION);
		queryRequestBean.setSign_type(PaymentConstant.SIGN_TYPE);
		queryRequestBean.setSign(SignUtil.genRSASign(JSON.parseObject(JSON.toJSONString(queryRequestBean))));
		String queryResult = HttpUtil.doPost("https://instantpay.lianlianpay.com/paymentapi/queryPayment.htm",
				JSON.parseObject(JSON.toJSONString(queryRequestBean)), "UTF-8");
		logger.info("实时付款查询接口响应报文：" + queryResult);
		if (StringUtils.isEmpty(queryResult)) {
			// 可抛异常，查看原因
			logger.error("实时付款查询接口响应异常");
			return;
		}
		QueryPaymentResponseBean queryPaymentResponseBean = JSONObject.parseObject(queryResult,QueryPaymentResponseBean.class);
		JSONObject repJson = JSONObject.parseObject(queryResult);
		// 先对结果验签
		boolean signCheck = TraderRSAUtil.checksign(PaymentConstant.PUBLIC_KEY_ONLINE, SignUtil.genSignData(repJson),queryPaymentResponseBean.getSign());
		if (!signCheck) {
			// 传送数据被篡改，可抛出异常，再人为介入检查原因
			logger.error("返回结果验签异常,可能数据被篡改");
			return;
		}
		if (queryPaymentResponseBean.getRet_code().equals("0000")) {
			 oidPaybill = queryPaymentResponseBean.getOid_paybill();//第三方订单号
			PaymentStatusEnum paymentStatusEnum = PaymentStatusEnum
					.getPaymentStatusEnumByValue(queryPaymentResponseBean.getResult_pay());
			String downMsg = paymentStatusEnum.getDesc();
			// TODO商户根据订单状态处理自已的业务逻辑
			switch (paymentStatusEnum) {
			case PAYMENT_APPLY:
				// 付款申请，这种情况一般不会发生，如出现，请直接找连连技术处理
				logger.error("出现放款申请状态，请联系连连技术人员处理：{}",queryPaymentResponseBean.no_order);
				 resultPay = "FAILURE";
				 downMsg = "出现放款申请状态，请联系连连技术人员处理：{"+queryPaymentResponseBean.no_order+"}";
				 loanService.automaticLoan(resultPay, oidPaybill, orderNo,downMsg);
				break;
			case PAYMENT_CHECK:
				// 复核状态 TODO
				// 返回4002，4003，4004时，订单会处于复核状态，这时还未创建连连支付单，没提交到银行处理，如需对该订单继续处理，需商户先人工审核这笔订单是否是正常的付款请求，没问题后再调用确认付款接口
				// 如果对于复核状态的订单不做处理，可当做失败订单
				submitPayment((String) queryPaymentResponseBean.getNo_order(), confirmCode);
				break;
			case PAYMENT_SUCCESS:
				 resultPay = "SUCCESS";//SUCCESS 付款成功 FAILURE 付款失败 CANCEL 付款退款
				loanService.automaticLoan(resultPay, oidPaybill, orderNo,downMsg);
				// 成功 TODO
				break;
			case PAYMENT_FAILURE:
				 resultPay = "FAILURE";
				 loanService.automaticLoan(resultPay, oidPaybill, orderNo,downMsg);
				// 失败 TODO
				break;
			case PAYMENT_DEALING:
				// 处理中 TODO
				break;
			case PAYMENT_RETURN:
				// 退款 TODO
				// 可当做失败（退款情况
				// 极小概率下会发生，个别银行处理机制是先扣款后打款给用户时再检验卡号信息是否正常，异常时会退款到连连商户账上）
				 resultPay = "FAILURE";
				 loanService.automaticLoan(resultPay, oidPaybill, orderNo,downMsg);
				break;
			case PAYMENT_CLOSED:
				// 关闭 TODO 可当做失败 ，对于复核状态的订单不做处理会将订单关闭
				 resultPay = "FAILURE";
				 loanService.automaticLoan(resultPay, oidPaybill, orderNo,downMsg);
				break;
			default:
				break;
			}
		}

	}

	public void submitPayment(String noOrder, String confirmCode) throws Exception {
		// 连连测试环境(商户测试期间需要用正式的数据测试，测试时默认单笔单日单月额度50，等测试OK，和连连技术核对过业务对接逻辑后，申请走上线流程打开额度）
		ConfirmPaymentRequestBean paymentRequestBean = new ConfirmPaymentRequestBean();
		paymentRequestBean.setNo_order(noOrder);// 付款流水号(全局唯一流水号，不会重复)
		// 当调用付款接口返回4002，4003，4004时，会返回验证码信息
		paymentRequestBean.setConfirm_code(confirmCode);// 验证码
		// 填写商户自己的接收付款结果回调异步通知 长度
		paymentRequestBean.setNotify_url(PaymentConstant.LIANLIAN_FK_URL);
		paymentRequestBean.setOid_partner(PaymentConstant.OID_PARTNER);// 商户号
		paymentRequestBean.setApi_version(PaymentConstant.API_VERSION);// api版本
		paymentRequestBean.setSign_type(PaymentConstant.SIGN_TYPE);// 加签方式RSA
		// 用商户自己的私钥加签
		paymentRequestBean.setSign(SignUtil.genRSASign(JSON.parseObject(JSON.toJSONString(paymentRequestBean))));
		String jsonStr = JSON.toJSONString(paymentRequestBean);
		// 用银通公钥对请求参数json字符串加密
		// 报Illegal key
		// size异常时，可参考这个网页解决问题http://www.wxdl.cn/java/security-invalidkey-exception.html
		String encryptStr = LianLianPaySecurity.encrypt(jsonStr, PaymentConstant.PUBLIC_KEY_ONLINE);

		if (StringUtils.isEmpty(encryptStr)) {
			// 加密异常
			logger.error("加密异常:");
			return;
		}
		JSONObject json = new JSONObject();
		json.put("oid_partner", PaymentConstant.OID_PARTNER);
		json.put("pay_load", encryptStr);
		String response = HttpUtil.doPost("https://instantpay.lianlianpay.com/paymentapi/confirmPayment.htm", json,
				"UTF-8");
		logger.info("确认付款接口返回响应报文：" + response);
		if (StringUtils.isEmpty(response)) {
		} else {
			ConfirmPaymentResponseBean confirmaymentResponseBean = JSONObject.parseObject(response,
					ConfirmPaymentResponseBean.class);
			JSONObject repJson = JSONObject.parseObject(response);
			// 对返回0000时验证签名
			if (confirmaymentResponseBean.getRet_code().equals("0000")) {
				// 先对结果验签
				boolean signCheck = TraderRSAUtil.checksign(PaymentConstant.PUBLIC_KEY_ONLINE,
						SignUtil.genSignData(repJson), confirmaymentResponseBean.getSign());
				if (!signCheck) {
					// 传送数据被篡改，可抛出异常，再人为介入检查原因
					logger.error("返回结果验签异常,可能数据被篡改");
					return;
				}
				logger.info(paymentRequestBean.getNo_order() + "确认放款订单处于付款处理中");
				// 已生成连连支付单，付款处理中（交易成功，不是指付款成功，是指跟连连流程正常），商户可以在这里处理自已的业务逻辑（或者不处理，在异步回调里处理逻辑）,最终的付款状态由异步通知回调告知
			} else if (RetCodeEnum.isNeedQuery(confirmaymentResponseBean.getRet_code())) {
				queryPaymentAndDealBusiness(paymentRequestBean.getNo_order(), repJson.getString("confirm_code"));
			} else {
				System.out.println("failure");
			}
		}
	}

	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void tbizflowHelp(TBizFlow bizFlow) {
		try {
			bizFlowService.save(bizFlow);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
